#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<fstream>
#include"util.hpp"
#include<mutex>
#include"Log.hpp"

namespace ns_index
{
    struct DocInfo//正排索引的
    {
        uint64_t doc_id;//文档id

        //文档内容：
        std::string title;//文档标题
        std::string content;//文档对应去标签的内容
        std::string url;//文档对应的url
    };

    struct InvertedElem//
    {
        uint64_t doc_id;
        std::string word;
        int weight;
    };

    //倒排拉链
    typedef std::vector<InvertedElem> InvertedList;
    

    class Index
    {
    public:
        ~Index()
        {}
       

        //根据doc_id找到文档内容
        DocInfo* GetForwardIndex(uint64_t doc_id)
        {
            if(doc_id>=forward_index.size())
            {
                std::cerr<<"doc_id out range,error!"<<std::endl;
                return nullptr;
            }
            return &(forward_index[doc_id]);
        }


        //根据关键字string，获得倒排拉链
        InvertedList* GetInvertedList(const std::string& word)
        {
            auto iter=Inverted_index.find(word);
            if(iter==Inverted_index.end())
            {
                std::cerr<<word<<"have no InvertedList"<<std::endl;
                return nullptr;
            }
            return &(iter->second);
        }
        



        //根据去标签，格式化之后的文档，构建正排索引与倒排索引
        //data/raw/raw.txt,
        bool BuildIndex(const std::string& input)//pase处理完毕的数据交给我
        {
            std::ifstream in;
            in.open(input.c_str(),std::ios::in|std::ios::binary);
            if(!in.is_open())
            {
                std::cerr<<"Error! open"<<input<<"failed"<<std::endl;
                return false;
            }
            std::string line;
            
            int i=0;

            while(std::getline(in,line))
            {
                DocInfo* doc=BuildForwardIndex(line);
                if(doc==nullptr)
                {
                    std::cerr<<"build"<<line<<"error"<<std::endl;//for debug
                    continue;
                }
                
                BuildInvertedIndex(*doc);
                i++;
                
                
                    //std::cout<<"当前已经建立的索引文档    "<<i<<std::endl;
                    LOG(NORMAL,"当前已经建立的索引文档: "+std::to_string(i));
                
            }
            return true;
        }

        //单例模式，注意线程安全：这里加锁
        static Index* GetInstance()
        {
            if(instance==nullptr)
            {
                mtx.lock();
                if(instance==nullptr)
                {
                    instance=new Index;
                }
                mtx.unlock();

            }
            return instance;
        }

    private:
        
        static Index* instance;
        static std::mutex mtx; 
        
        Index()
        {}

        Index(const Index& )=delete;
        Index& operator=(const Index& )=delete;
    public:        
        DocInfo* BuildForwardIndex(const std::string& line)
        {
           //解析line,将line分成三部分：title，content，urll
           //line - > 3 string title content url 
            const std::string sep="\3";//行内分隔符：用来分离不同html文件的内容
            std::vector<std::string> result;
            ns_util::StringUtil::Split(line,&result,sep);//函数对line进行解析，并把解析内容放到result中
            if(result.size()!=3)
            {
                return nullptr;
            }

            //字符串进行填充到DocInfo
            DocInfo doc;
            doc.title=result[0];
            doc.content=result[1];
            doc.url=result[2];
            doc.doc_id=forward_index.size();
            forward_index.push_back(std::move(doc));
            return &forward_index.back(); 

        }

        bool BuildInvertedIndex(const DocInfo& doc)
        {

            std::vector<std::string> title_words;

            //对文档标题进行分词
            ns_util::JiebaUtil::CutString(doc.title,&title_words);

            std::vector<std::string> content_words;
            //对文档内容进行分词
            ns_util::JiebaUtil::CutString(doc.content,&content_words);
            
            struct word_cnt
            {
                int title_cnt;
                int content_cnt;
                word_cnt() :title_cnt(0),content_cnt(0)
                {}
            };

            std::unordered_map<std::string,word_cnt>word_map;//暂时用来存放词频的映射表
            
            //对文档标题进行词频统计
            for(auto x:title_words)
            {
                boost::to_lower(x);//这里我们规定在搜索时不去分大小写，所以这里统一转化成小写
                word_map[x].title_cnt++;//如果存在就获取，不存在就建立
            }
            
            //对文档内容进行词频统计
            for(auto x: content_words)
            {
                boost::to_lower(x);
                word_map[x].content_cnt++;
            }
#define X 10
#define Y 1
            for(auto s:word_map)
            {
                InvertedList& list=Inverted_index[s.first];
                InvertedElem item;
                item.doc_id=doc.doc_id;
                item.word=s.first;
                item.weight=s.second.title_cnt*X+s.second.content_cnt*Y;
                list.push_back(std::move(item));
            }
            return true;
        }



    private:
        //正排索引的数据结构用数组，数组的下标就是文档id
        std::vector<DocInfo> forward_index;
        //倒排索引一定是一个关键字对应一组（个）InvertedElem
        std::unordered_map<std::string,InvertedList> Inverted_index;
    };
Index* Index::instance=nullptr;
std::mutex Index::mtx;

}
